문서의 임의 삭제는 제재 대상으로, 문서를 삭제하려면 삭제 토론을 진행해야 합니다. 문서 보기문서 삭제토론 참조에 의한 호출 (문단 편집) === 상세한 설명 === 많은 초보자들이 주소에 의한 호출과 참조에 의한 호출을 헷갈려하는데 이건 [[C언어]]의 포인터 문법의 난해함이 크게 기여했다. 사실 C의 모든 함수는 값에 의한 호출'''만''' 한다. 즉 값에 의한 호출만 사용한다는 것. 값으로 주소값을 전달하느냐 값 자체를 전달하느냐의 차이인데 포인터를 이용해서 결과적으로 참조에 의한 호출과 동일한 효과를 낼 뿐이다. 여기에 포인터 연산자인 *가 선언할 때와 사용할 때의 용법이 반대라는 점, 참조 연산자 &가 더해졌다는 점이 이러한 혼란을 가중시켰다. 값에 의한 호출, 참조에 의한 호출, 주소에 의한 호출 모두 매개변수에 '''값'''을 전달하는 건 똑같다. 값에 의한 호출은 명백하니 넘어가고, 주소에 의한 호출은 함수의 '''호출''' 측에서는 값을 전달하는 것처럼 보이나 받는 쪽에서는 [[포인터]]로 받는다는 것, 그리고 참조에 의한 호출은 함수의 호출 측에서 처음부터 포인터를 '''명시적으로''' 전달한다는 것의 차이가 있다. 사실 컴파일러가 컴파일한 뒤의 결과는 똑같다.[* 컴파일러는 항상 주소값으로 연산을 하기 때문이다. 위에서 예로 든 좌표 찍기 코드(functionC)의 경우도 int& a를 int* a로 바꾸고, (&a + 1)을 (a + 1)로 바꾸면 결과는 동일하게 나온다. 참조에 의한 호출은 아니지만 명시적인 주소 값으로 호출을 하기 때문에 최종적으로 같은 결과가 출력되는 것이다.] 단지 함수 선언부에서 문법을 포인터로 선언했냐, 참조자로 선언했냐의 차이 뿐이다. 상기 단락에서 서술한 예시를 주소에 의한 호출로 고쳐보자. {{{#!syntax cpp void functionD(int* v) { (*v)++; } int main(void) { int a = 10; functionD(&a); printf("%d", a); } }}} > 결과값: 11 포인터를 공부했다면 익숙하게 접할 수 있는 코드인데, 이를 두고 참조에 의한 호출이라고 말하는 실수를 많이 저지른다. 왜 이것이 잘못된 표현인지는 연산자가 하는 일을 주의 깊게 따라가면 알 수 있다. 컴파일러는 변수를 다룰 때 변수의 타입과 메모리 맵 상의 배치([[포인터]] 주소)를 기억한다. 값 자체는 컴파일러가 처리하는 게 아니라 [[운영체제|OS]]가 프로그램을 메모리에 적재할 때 컴파일러가 만들어 둔 메모리 맵을 보고 포인터가 가리키는 실제 주소에 값을 적재한다. 따라서 v라는 변수를 선언했다면 세 가지 정보를 뽑아낼 수 있다. 하나는 v의 값(v), 다른 하나는 v의 주소(&v), 그리고 마지막으로 v의 타입(typeof(v)). 마지막 typeof는 함수처럼 생겼지만 연산자이고 키워드의 일종이다. --이걸 보면 C언어의 문법적 해괴함이 좀 와닿을 것이다.-- * 주소 참조 연산자인 & 연산자는 a의 값이 아니라 a의 주소를 반환하는 연산자이다. 그런데 '''선언'''할 때는 주소를 반환하라는 의미가 아니라 '''해당 타입의 [[참조자]]([[C++]]에서)를 사용'''한다는 의미로 바뀌어 버린다. * int *v 의 의미는 v라는 이름의 '''포인터 변수'''를 선언한다는 의미이다. 이 포인터 변수의 값(v)은 64비트 정수값(메모리 주소)[* 리눅스 커널, 64bit OS 기준.]으로 이루어지며 이 포인터 변수 자체의 주소(&v)는 '''따로''' 존재한다.[* 그런 고로 이 포인터 변수 자체의 주소를 다른 포인터 변수의 주소와 스왑하고자 할때 이중 포인터를 써야 한다.] * typeof(v)는 int * 타입(int의 포인터 타입)으로 설정된다.[* 그래서 sizeof(int)는 4인데(32비트) sizeof(int *)는 8이다(64비트).] 하지만 선언문이 아닌 곳에서 *v의 의미는 '''v라는 포인터 변수가 가리키는 주소를 찾아가서 해당 값을 읽어라'''이다. 즉 선언할 때와 사용할 때의 문법이 반대이다. 여기까지 잘 따라왔다면 포인터에 대해 어느 정도 이해가 됐을 것이다. --컴퓨터공학 전공자라고 해도 [[어셈블리어]] 과목을 A학점 이상으로 이수하기 전에는 힘들다.-- main 함수에서는 일반 값 변수인 a를 선언했다. 따라서 컴파일러는 main 함수의 스택 프레임에 변수 a의 메모리 맵을 작성해 두었다. 즉 실제 값이 저장된 공간이 main 함수의 스택 프레임에 생겨났다. 다음으로, functionD는 int의 '''포인터'''를 매개변수로 선언하였다. 윗 문단의 functionB에서는 &b 형태의 매개변수를 선언했는데 이 형태는 컴파일러가 내부적으로 포인터 매개변수로 바꿔 처리하게 되어 있다. 그냥 프로그래머한테 주소 변환 및 역변환 절차를 안 보이게 가려놨을 뿐이다. 함수 호출시에 &v로 선언한 functionB 코드에서는 그냥 a를 전달했지만 *v로 선언한 이번 코드에서는 &a를 전달한다. &a는 변수 a의 주소를 반환하는 연산자라고 설명한 바 있다. functionD가 선언할 때 '''int 타입의 포인터'''를 받게 선언했으므로 포인터 값을 프로그래머가 '''명시적으로''' 전달한 것이다. 참조자로 선언한 코드는 컴파일러가 이 포인터 변환 연산을 처리하지만 포인터로 선언한 코드는 프로그래머가 명시적으로 해 줘야 한다. 보이는 결과값만으로는 분명 참조에 의한 호출과 동일하고, 동작도 비슷해보이겠지만 실제로는 다르다. 예를 들어, main 함수에 정의된 int a의 주소값이 0x1000이라고 치자면, 참조에 의한 호출 예시에서는 functionB의 int& v의 주소값도 0x1000이다. 하지만 주소에 의한 호출 예시에서 functionD의 int* v는 '''functionD 스택에서 정의된 새로운 포인터 변수이며, int* v의 주소값은 0x1000이 아닌, functionD에서 생성된 새로운 주소값(이를테면 0x1012 등)이고, 이 변수에 저장된 값이 0x1000인 것이다.''' 즉, functionD 함수에서 매개변수로 정의된 int* v에 main 함수의 a의 주소값을 '''복사'''함으로서 매개변수 v가 main 함수의 a에 접근할 수 있는 것. 반면 [[참조자]]는 참조 대상과 주소 값이 같다는 특성 때문에 참조 대상의 '별칭(alias)'이라고도 불린다. C++의 창시자 비아르네 스트로우스트루프가 쓴 에도 "A reference is an alternative name for an object."라고 적혀 있다. 어떤 함수의 매개변수가 int& ref로 선언되고 main 함수에서는 이 함수에 int형 변수 num을 인자로 전달한다고 할 때, 함수 내에서 참조자 ref에 ++ 연산을 하면 main 함수의 num이 ref와 같은 메모리 공간을 나타내므로 num의 값이 1 증가하게 된다. 그럼 참조자로 선언하면 0x1012에 해당하는 새로운 변수가 할당되지 않는 걸까? 할당된다. 컴파일러가 알아서 처리하는 익명의 포인터 변수여서 프로그래머가 접근할 수 없을 뿐이다. 새로 만들어지는 익명 변수의 이름을 PTR이라고 가정했을 때, 프로그램 내부에서 int& ref = num;을 만나면 컴파일러에 의해 ref와 1:1로 대응되는 포인터 변수 PTR이 생성되고 연산식은 const int* PTR = #으로 바뀌어 계산된다.[* PTR은 const 포인터이므로, PTR을 통해 num의 값을 변경하는 것이 불가능하다. 프로그래머는 PTR에 접근할 수 없는데 PTR이 내부에서 알 수 없는 과정으로 num의 값을 변경시키면 문제가 생기니 const 선언을 하여 이를 막아놓은 것.] 따라서 ref++;는 결과적으로 (*PTR)++;가 된다고 할 수 있다. 만약 함수 내에서 ref의 주소값(=num의 주소값)을 출력하기 위해 cout << &ref;를 입력하면, 실제로 컴파일러가 계산하는 것은 cout << PTR;인 것이다. 정리를 하자면 [[C언어|C]]의 주소에 의한 호출은 명시적인 포인터를 통한 호출을 의미하며, 본질적으로는 값에 의한 호출이지만 그 '값'이 주소값이기에 구분을 위하여 '주소에 의한 호출'이라고 부르는 것이다. 주소에 의한 호출은 아랫단락에 서술될 인자값 비교를 제외하면 참조에 의한 호출과 동일한 결과를 낼 수 있다. 하지만 이건 어디까지나 '''흉내를 낸 것'''이고, 함수 내에서 인자값을 변화시키는 것이 불가능하기에 C에는 참조에 의한 호출이 존재하지 않는다고 해야 한다. 실제로 참조에 의한 호출를 행하는 것은 [[C++]]의 참조자이며 참조자 역시 내부적으로는 (const) 포인터로 변환되어 동작하니, 결국 포인터와 참조자 모두 주소값을 '''복사'''하여 대상에 접근한다는 공통점이 있다.저장 버튼을 클릭하면 당신이 기여한 내용을 CC-BY-NC-SA 2.0 KR으로 배포하고,기여한 문서에 대한 하이퍼링크나 URL을 이용하여 저작자 표시를 하는 것으로 충분하다는 데 동의하는 것입니다.이 동의는 철회할 수 없습니다.캡챠저장미리보기